import { UsePlugin, GetProps, GetState } from '../utils/usePlugin';
import { WebPlugin } from '../plugins/Plugin';
import { WebController } from './WebController';
import { PluginManager } from './PluginManager';

export interface WebRouterOptions<T extends UsePlugin, P> {
  plugins?: T;
  prefix?: string;
  routes(this: P): void;
}

type WebRouter<ExtraProps = {}, ExtraState = {}> = WebRouterFunc<ExtraProps, ExtraState> & {
  global: WebRouterGlobal<ExtraProps, ExtraState>;
  local: WebRouterLocal<ExtraProps, ExtraState>;
};

type WebRouterFunc<ExtraProps = {}, ExtraState = {}> = {
  <
    T extends UsePlugin,
    P = WebController<GetProps<T> & ExtraProps, GetState<T> & ExtraState>
  >(options: WebRouterOptions<T, P>): WebController;
}

type WebRouterGlobal<ExtraProps = {}, ExtraState = {}> = <Props, State>(plugins: UsePlugin<Props, State> | WebPlugin<Props, State>) => WebRouter<Props & ExtraProps, State & ExtraState>;

type WebRouterLocal<ExtraProps = {}, ExtraState = {}> = <Props, State>(plugins: UsePlugin<Props, State> | WebPlugin<Props, State>) => WebRouterFunc<Props & ExtraProps, State & ExtraState> & {
  local: WebRouterLocal<Props & ExtraProps, State & ExtraState>;
};

export const baseWebRouter: WebRouter = function (this: PluginManager | null, options) {
  const manager = this instanceof PluginManager
    ? this.clone()
    : new PluginManager();
  const router = new WebController(manager);

  manager.appendLocalPlugins(options.plugins);
  router.setPrefix(options.prefix);
  router.setActions(options.routes);

  return router;
}

baseWebRouter.global = function (this: PluginManager | null, plugins) {
  const manager = this instanceof PluginManager
    ? this.clone()
    : new PluginManager();
  const webRouter = baseWebRouter.bind(manager);

  manager.appendGlobalPlugins(plugins);
  webRouter.global = baseWebRouter.global.bind(manager);
  webRouter.local = baseWebRouter.local.bind(manager);

  return webRouter;
};

baseWebRouter.local = function (this: PluginManager | null, plugins) {
  const manager = this instanceof PluginManager
    ? this.clone()
    : new PluginManager();
  const webRouter = baseWebRouter.bind(manager);

  manager.appendLocalPlugins(plugins);
  webRouter.local = baseWebRouter.local.bind(manager);

  return webRouter;
};
